/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package game.experts.combine;

import analysys.HECAlignmentHit;
import game.datahandling.dataenvelopes.base.PrimaryStructure;
import game.encodings.OutputEncoding;
import game.experts.Expert;
import java.util.ArrayList;
import java.util.List;
import utils.ArrayUtils;
import utils.Profile;
import utils.ProfileMatrix;

/**
 *
 * @author emanuele
 */
public class SSPSlidingWindowHintedCombinerExpert extends SSPBaseHintedCombinerExpert {
    public int sideResidues = 4;

    @Override
    protected Profile mergePredictions(OutputEncoding[] allPredictions) {
        Profile ret;

        ret = new ProfileMatrix(3, this.current.input.length(), 0);
        for (int i = 0; i < allPredictions.length; i++) {
            double[] weights = this.getWeightsForExpert(this.experts.get(i));
            Profile p = this.applyWeightsToProfile(allPredictions[i].getProfile(), weights);

            ret.add(p);
        }
        ret.normalizeColumns();

        return ret;
    }

    protected double[] getWeightsForExpert(Expert e) {
        double[] weights = new double[this.current.input.length()];

        List<String> alignedHintsPredictions = this.getAlignedHintsPredictions(e);

        int windows = this.current.input.length() - 2*this.sideResidues;
        for (int win = 0; win < windows; win++) { // Per ogni finestra:
            int idx = win + this.sideResidues; // L'indice in weights[]

            if (this.current.hints.isEmpty()) { // Non ci sono hint, setta tutto a 1.0
                weights[idx] = 1.0;
                continue;
            }

            weights[idx] = 0.0;

            for (int hintIdx = 0; hintIdx < this.current.hints.size(); hintIdx++) {
                double weightForHint; // Il peso nella posizione $idx-esima per questo hint
                String hintsec = this.current.hints.get(hintIdx).hitAlignedSec;
                String hintpre = alignedHintsPredictions.get(hintIdx);

                int startIdx = win;
                int endIdx = win + 1 + 2*this.sideResidues; // |-side-A-side-|
                double realLength = 0.0;

                for (int i = startIdx; i < endIdx; i++) {
                    if (hintpre.charAt(i) != '-')
                        realLength++;
                }

                if (realLength < 1.0) {
                    // La finestra contiene solo '-', quindi il peso è uno.
                    weightForHint = 1.0;
                } else {
                    double errors = this.getDifferences(hintpre, hintsec, startIdx, endIdx);
                    errors += 0.5; // Per evitare errorRate == 0

                    double errorRate = errors/realLength;

                    weightForHint = this.weightFromErrorRate(errorRate);
                }

                // Somma i pesi:
                weights[idx] += weightForHint;
            }
        }

        for (int i = 0; i < this.sideResidues; i++) {
            // I primi (e gli ultimi) $sideResidues pesi sono uguali al primo (ultimo) peso
            // calcolato.
            weights[i] = weights[this.sideResidues];
            weights[weights.length-(i+1)] = weights[weights.length-(this.sideResidues+1)];
        }

        return weights;
    }

    protected Profile applyWeightsToProfile(Profile profile, double[] weights) {
        for (int i = 0; i < profile.length(); i++)
            profile.setColumn(i, ArrayUtils.mulBy(profile.getColumn(i), weights[i]));
//        System.out.println(profile.toString());
        return profile;
    }

    protected List<String> getAlignedHintsPredictions(Expert e) {
        List<String> ret = new ArrayList<String>();

        for (HECAlignmentHit hint: this.current.hints) {
            // phws = Prediction of Hit Whole Secondary
            String phws = e.predict(new PrimaryStructure(hint.hitWholePri)).decode().toString();
            // phas = Prediction of Hit Aligned Secondary
            StringBuffer phas = new StringBuffer("");
            int j = hint.hitAlignmentStartIndex;
            for (int i = 0; i < hint.hitAlignedSec.length(); i++) {
                if (hint.hitAlignedSec.charAt(i) == '-') {
                    phas.append('-');
                } else {
                    phas.append(phws.charAt(j));
                    j++;
                }
            }
            ret.add(phas.toString());
        }

        return ret;
    }

}
